#include <header.h>
#include "client.h"
#include "long_cmd.h"
//typedef struct train_s{
//    int length; //  火车头 固定4Bytes
//    int _cmdType;   //  命令类型
//    char buff[1024];    //  火车车厢 上限为1024byte
//}train_t;
//
//train_t entrain(char* str,int len){    //  根据字符串创建小火车
//    train_t train;
//    train.length = len;
//    memcpy(train.data,str,len);
//    return train;
//}
void splitString(const char * pstrs, const char* delimiter, char *tokens[], int max_tokens, int * pcount){  //  pstrs:待处理字符串  delimiter:分段标识符   
                                                                                                            //  token[]:存放拆分的词组  pcount:拆分后词组的个数
    int token_count = 0;
    char *token = strtok((char *)pstrs, delimiter); // 使用delimiter作为分隔符

    while (token != NULL && token_count < max_tokens - 1) { // 保留一个位置给NULL终止符
        char * pstr = (char*)calloc(1, strlen(token) + 1);
        strcpy(pstr, token);
        tokens[token_count] = pstr;//保存申请的堆空间首地址
        token_count++;
        token = strtok(NULL, delimiter); // 继续获取下一个token
    }
    // 添加NULL终止符
    tokens[token_count] = NULL;
    *pcount= token_count;
}

int getCommandType(const char * str)
{
    if(!strcmp(str, "pwd"))
    {
        return CMD_TYPE_PWD;
    }  
    else if(!strcmp(str, "ls"))
    {
        return CMD_TYPE_LS;
    }   
    else if(!strcmp(str, "cd"))
    {
        return CMD_TYPE_CD;
    }
    else if(!strcmp(str, "mkdir"))
    {
        return CMD_TYPE_MKDIR;
    }
    else if(!strcmp(str,"rmdir"))
    {
        return CMD_TYPE_RMDIR;
    }   
    else if(!strcmp(str, "puts"))
    {
        return CMD_TYPE_PUTS;
    }
    else if(!strcmp(str, "gets"))
    {
        return CMD_TYPE_GETS;
    }
    else
    {
        return CMD_TYPE_NOTCMD;
    }
}
//解析命令
int parseCommand(const char * pinput, train_t * pt)
{
    char * pstrs[10] = {0};
    int cnt = 0;
    splitString(pinput, " ", pstrs, 10, &cnt);
    pt->_cmdType = getCommandType(pstrs[0]);
    //暂时限定命令行格式为：
    //1. cmd
    //2. cmd content
    if(cnt > 1) {
        pt->length = strlen(pstrs[1]);
        strncpy(pt->buff, pstrs[1], pt->length);
    }
    return 0;
}

int splitcmd(const char *pinput,const char *delimiter,char *tokens[],int max_tokens,int  *pcount){
    int token_count = 0;
    char *token = strtok((char *)pinput,delimiter);

    while(token!=NULL && token_count < max_tokens-1){
        char *pstr = (char*)calloc(1,strlen(token)+1);
        strcpy(pstr,token);
        printf("Current token is :[%s]\n",token);
        tokens[token_count++]=pstr;
        token = strtok(NULL,delimiter);
    }
    tokens[token_count]=NULL;
    *pcount = token_count;
    for(int i=0;i<*pcount;i++){
        printf("tokens[%d] = %s\n",i,tokens[i]);
    }
    return 0;
}

const char cmd_cmp[][15] ={"cd","ls","pwd","puts","gets","rm","mkdir","register","sign_in"};     //  为3~6时需要传文件名,7为注册信息，8为登录信息
#define STR_LEN 8 //定义随机输出的字符串长度。

/* Usage: */
int main(int argc ,char *argv[])
{
    //  ./client 个人ip地址输入，如：192.168.217.128 约定端口号，如：9876
    ARGS_CHECK(argc,3);

    // ---------------------------- 连接服务器-------int parseCommand(const char * pinput, int len, train_t * pt)----------------------- //
    int sockfd = socket(AF_INET,SOCK_STREAM,0);

    //  创建连接服务器的地址和端口信息
    struct sockaddr_in sockaddr;
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_addr.s_addr= inet_addr(argv[1]);
    sockaddr.sin_port = htons(atoi(argv[2]));

    //  建立socket连接
    int ret = connect(sockfd,(struct sockaddr *)&sockaddr,sizeof(sockaddr));
    ERROR_CHECK(ret , -1 ,"connect");
    if(ret==0){
        printf("connect successed!please input cmd:\n");
    }    
    // ---------------------- 与服务器进行命令交互 ------------------------ //
    //
    //  创建epoll文件对象
    int epfd = epoll_create(1);
    //  创建epoll监听事件集合
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = STDIN_FILENO;
    //  添加监听事件：标准输入
    epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&event);
    //  添加监听事件：socket的接收数据（读就绪）
    event.data.fd = sockfd;
    epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event);

    while(1){
        //  构建就绪接受数组
        struct epoll_event events[10];
        //  开始监听
        printf("enter epoll loop\n");   
        int num = epoll_wait(epfd,events,10,-1);
        for(int i=0;i<num;i++){     //  遍历就绪集合
            int cfd = events[i].data.fd;
            if(cfd == STDIN_FILENO){
                // ****************** 标准输入就绪 ******************* //
                char buf[64]={0};
                int flag=0 ;       //  有效命令标识符，1：输入命令有效 0：输入命令无效
                ssize_t sret;
                int check_cmd=-1;
                train_t send_train ;
                while(!flag){
                    flag = 0;
                    bzero(buf,sizeof(buf));

                    //  读标准输入
                    sret = read(STDIN_FILENO,buf,sizeof(buf));
                    if(buf[sret-1]=='\n'){
                        buf[sret-1]='\0';
                    }

                    printf("buf: %s, buf's len:%ld\n", buf, strlen(buf));


                    char * pstr[20] = {0};
                    int cnt = 0;
                    splitcmd(buf," \n",pstr,20,&cnt);

                    printf("phrase cnt = %d, pstr[0]=%s, pstr[0]'s len:%ld\n",cnt,pstr[0],strlen(pstr[0]));

                    if(cnt<3){
                        for(check_cmd=0;check_cmd<8;check_cmd++){
                            if(strncmp(pstr[0],cmd_cmp[check_cmd],strlen(pstr[0]))==0){ //  检验输入是否有效

                                flag=1;
                                send_train._cmdType=check_cmd;
                                printf("cmd = %s\n",cmd_cmp[check_cmd]);

                                //  write(STDOUT_FILENO,buf,sizeof(buf));
                                break;
                            }
                        }
                        if(cnt==2){
                            if(check_cmd>2 && check_cmd<7){     //  传文件相关命令时，分两段
                                send_train.length = strlen(pstr[1]);
                                strncpy(send_train.buff,pstr[1],send_train.length);
                                //char filename[20]={0};
                                //strcpy(filename,pstr[1]);
                                //write(STDOUT_FILENO,filename,sizeof(filename));
                            }
                        }
                    }
                    if(flag == 0){
                        printf("输入无效，请重新输入：\n");
                    }

                }
                //  读字符串装载小火车
                //train_t send_train ;
                //parseCommand(buf,&send_train);
                //send_train.length= sret;
                //send_train._cmdType=check_cmd;
                //if(check_cmd ==3){
                //puts(sockfd,send_train.buff);
                if(check_cmd == 3){     //  输入命令为puts时
                    int puts_rec = puts_client(sockfd,send_train.buff);
                    ERROR_CHECK(puts_rec,-1,"puts");
                    if(puts_rec == 0){
                        printf("file uploaded successed!!!\n");
                    }
                    //    int total = put_name(send_train.length,send_train.buff);     //  获取上传文件内容长度
                    //    int num = 0;
                    //    while(num<total){

                    //        train_t file_train;
                    //        if(num+1000>total){
                    //            file_train.length = total-num;
                    //        }
                    //        else{
                    //            file_train.length = 1000;
                    //        }
                    //        file_train.buff = put_file(send_train.buff,num);
                    //        
                    //        send(sockfd,&file_train,sizeof(file_train.length)+file_train.length+sizeof(file_train._cmdType),MSG_WAITALL);
                    //        num+=file_train.length;
                    //    }
                }
                //  发送对端
                else{
                    int send_cmd_ret = send(sockfd,&send_train,sizeof(send_train.length)+send_train.length+sizeof(send_train._cmdType),MSG_WAITALL);
                    ERROR_CHECK(send_cmd_ret,-1,"send send_train");
                }

                // if(check_cmd == 3){     //  输入命令为puts时
                //     int total = put_name(send_train.length,send_train.buff);     //  获取上传文件内容长度
                //     int num = 0;
                //     while(num<total){

                //         train_t file_train;
                //         if(num+1000>total){
                //             file_train.length = total-num;
                //         }
                //         else{
                //             file_train.length = 1000;
                //         }
                //         file_train.buff = put_file(send_train.buff,num);
                //         
                //         send(sockfd,&file_train,sizeof(file_train.length)+file_train.length+sizeof(file_train._cmdType),MSG_WAITALL);
                //         num+=file_train.length;
                //     }
                // }
            }
            else if(cfd == sockfd){     
                // *********************************从服务器接收文件时********************************  //
                // 创建一个小火车接收文件名/命令返回值
                train_t rec_train;
                //  先收小火车
                int recv_train_length_ret = recv(sockfd,&rec_train.length,sizeof(rec_train.length),MSG_WAITALL);      // 先收火车头
                ERROR_CHECK(recv_train_length_ret,-1,"recv train.length");
                int recv_train_cmdType_ret = recv(sockfd,&rec_train._cmdType,sizeof(rec_train._cmdType),MSG_WAITALL);     //  再收火车类型
                ERROR_CHECK(recv_train_cmdType_ret,-1,"recv train._cmdType");
                int recv_train_buff_ret = recv(sockfd,rec_train.buff,rec_train.length,MSG_WAITALL);      //  再收火车车厢
                ERROR_CHECK(recv_train_buff_ret,-1,"recv train.buff");
                //  对端数据到达，socket读就绪
                char buf[1000]={0};

                memcpy(buf,rec_train.buff,rec_train.length);    //  从车厢中取出文件名/命令指令
                if(rec_train._cmdType==4){      //  读取的为gets命令返回数据时
                                                //gets(sockfd,buf);
                                                //  车厢中的内容为文件
                                                //  int fd = open(buf,O_RDWR|O_CREAT|O_TRUNC,0666);     //  创建一个文件名为buf中内容的文件
                                                //  bzero(buf,sizeof(buf));
                                                //  //  创建一个小火车接收文件
                                                //  train_t rec_file;       
                                                //  while(rec_file.length!=0){
                                                //      bzero(rec_file.buff,sizeof(rec_file.buff));
                                                //      recv(sockfd,&rec_file.length,sizeof(rec_file.length),0);  //  先收火车头
                                                //      recv(sockfd,rec_file.buff,rec_file.length,MSG_WAITALL);     //  在接收火车车厢
                                                //      write(fd,rec_file.buff,rec_file.length);
                                                //}
                                                //int filesize = gets_clients(socket,filename);             //  下载文件接口
                    int gets_rec = gets_client(sockfd,&rec_train);
                    ERROR_CHECK(gets_rec,-1,"gets");
                    if(gets_rec == 0){
                        printf("file download successed!!!\n");
                    }
                    // //  车厢中的内容为文件
                    // int fd = open(buf,O_RDWR|O_CREAT|O_TRUNC,0666);     //  创建一个文件名为buf中内容的文件
                    // bzero(buf,sizeof(buf));
                    // //  创建一个小火车接收文件
                    // train_t rec_file;       
                    // while(rec_file.length!=0){
                    //     bzero(rec_file.buff,sizeof(rec_file.buff));
                    //     recv(sockfd,&rec_file.length,sizeof(rec_file.length),0);  //  先收火车头
                    //     recv(sockfd,rec_file.buff,rec_file.length,MSG_WAITALL);     //  在接收火车车厢
                    //     write(fd,rec_file.buff,rec_file.length);
                    // }
                    //     int filesize = gets_clients(socket,filename);             //  下载文件接口
                    //     recvFile(sockfd,)
                }
                /* else if(rec_train._cmdType==7){     //  读取的为register类型时 */

                /* } */
                else if(rec_train._cmdType == 8){   //  读取的为sign_in类型时
                    char mode[32]={0};
                    int flag = 0;       //  模式选择    0：未选中模式       1：注册模式         2：登录模式
                    do{
                        bzero(mode,sizeof(mode));
                        printf("请选择登录模式，如输入“register”或“sign_in”:\n");     
                        ssize_t mret = read(STDIN_FILENO,mode,sizeof(mode));
                        if(mode[mret-1]=='\n'){
                            mode[mret-1]='\0';
                        }
                        if(strncmp(mode,"register",mret)){
                            flag = 1;
                        }
                        else if(strncmp(mode,"sign_in",mret)){
                            flag = 2;
                        }
                        else{
                            printf("模式选择失败，请重新选择登入模式！\n");
                        }
                    }while(flag==0);

                    train_t sign_train;
                    char buf[256]={0};       //  IO设备缓存区，用于暂存输入的用户名+密码
                                             //  ································ 用户注册功能 ···································· //
                    if(flag == 1){
                        int name_usable = 1;
                        ssize_t uret;
                        do{
                            bzero(buf,sizeof(buf));
                            name_usable=1;      //  检验是否为可用用户名：中间不能出现空格      name_usable=0：不可用       1：可用
                            printf("请输入用户名：\n");
                            uret = read(STDIN_FILENO,buf,sizeof(buf));
                            if(buf[uret-1]=='\n'){
                                buf[uret-1]='\0';
                            }
                            for(int i=0;i<uret-1;i++){
                                if(buf[i]=='\0'){
                                    name_usable = 0;
                                    break;
                                }    
                            }
                            if(name_usable==0){
                                printf("用户名不可用，用户名中间不能出现空格，请重新输入！\n");
                            }
                        }while(name_usable == 0);

                        //  ##################################  发送注册用户名  ###################################  //
                        bzero(sign_train.buff,sizeof(sign_train.buff));
                        strncpy(sign_train.buff,buf,uret);
                        sign_train.length = uret;
                        sign_train._cmdType = 7;
                        int register_name_ret = send(sockfd,&sign_train,sizeof(sign_train.length)+sizeof(sign_train._cmdType)+sign_train.length,MSG_WAITALL);
                        ERROR_CHECK(register_name_ret,-1,"send register_name");

                        //  #################################   接收注册用户名反馈并判断是否可用 #####################################  //
                        train_t rec_reg_name_train;
                        int recv_register_name_train_length_ret = recv(sockfd,&rec_reg_name_train.length,sizeof(rec_reg_name_train.length),MSG_WAITALL);
                        ERROR_CHECK(recv_register_name_train_length_ret,-1,"recv reg_name_train.length");
                        int recv_register_name_train_cmdType_ret = recv(sockfd,&rec_reg_name_train._cmdType,sizeof(rec_reg_name_train._cmdType),MSG_WAITALL);
                        ERROR_CHECK(recv_register_name_train_cmdType_ret,-1,"recv reg_name_train._cmdType");
                        int recv_register_name_train_buff_ret = recv(sockfd,rec_reg_name_train.buff,rec_reg_name_train.length,MSG_WAITALL);
                        ERROR_CHECK(recv_register_name_train_buff_ret,-1,"recv reg_name_train.buff");

                        char * rpstr[20] = {0};
                        int cnt = 0;
                        splitcmd(rec_reg_name_train.buff," \n",rpstr,20,&cnt);

                        printf("phrase cnt = %d,rpstr[1] = %s,rpstr[1]'s len:%ld\n",cnt,rpstr[1],strlen(rpstr[1]));

                        if(cnt==2 && strcpy(rpstr[1],"available")==0){
                            printf("用户名可用\n");
                        }
                        else if(cnt==2 && strcpy(rpstr[1],"failed")==0){
                            printf("该用户名已注册！\n");
                            name_usable = 0;
                        }
                        else{
                            printf("服务端传输错误！\n");
                        }

                        if(name_usable){        //  注册用户名通过验证，输入该用户名盐值和密码
                            int password_usable = 1;
                            ssize_t pret;
                            do{
                                bzero(buf,sizeof(buf));
                                password_usable = 1;
                                printf("请输入密码(密码由数字、字母和符号组成，至少包含其中两种，不能出现空格，如：a123)：\n");
                                pret = read(STDIN_FILENO,buf,sizeof(buf));
                                if(buf[pret-1]=='\n'){
                                    buf[pret-1]='\0';
                                }
                                int alph_flag = 0;      //  记录是否出现过字母
                                int num_flag = 0;           //  记录是否出现过数字
                                int symbol_flag = 0;    //  记录是否出现过符号
                                for(ssize_t i=0;i<pret-1;i++){
                                    if(buf[i]=='\0'){
                                        printf("密码不能出现空格!\n");
                                        continue;
                                    }
                                    else if(buf[i]>='0' && buf[i]<='9'){
                                        num_flag=1;
                                    }
                                    else if((buf[i]>='a' && buf[i] <= 'z')||(buf[i]>='A' && buf[i] <='Z')){
                                        alph_flag = 1;
                                    }
                                    else{
                                        symbol_flag = 1;
                                    }
                                }
                                if(alph_flag+num_flag+symbol_flag<2){
                                    password_usable = 0;
                                    printf("密码不符合要求！密码需由数字、字母和符号中的两种组成。\n");
                                }
                            }while(password_usable==0);
                            char password[64]={0};
                            strcat(password,buf);
                            printf("%s\n",buf);

                            //  =============================== 生成8位随机盐值 ============================    //
                            char salt[STR_LEN + 1] = {0};
                            srand(time(NULL));
                            for(int i = 0; i < STR_LEN; i ++) {
                                int tmp_flag = rand()%3;
                                switch(tmp_flag) {
                                case 0: salt[i] = rand()%26 + 'a'; break;
                                case 1: salt[i] = rand()%26 + 'A'; break;
                                case 2: salt[i] = rand()%10 + '0'; break;
                                }
                            }
                            printf("%s\n", salt);//输出生成的8位随机盐值。
                            train_t salt_train;
                            salt_train.length = 9;
                            salt_train._cmdType = 7;
                            strcat(salt_train.buff,buf);
                            
                            char cryptpasswd[128]={0};
                            strcat(cryptpasswd,crypt(buf,salt));
                            printf("encrypted password = %s\n",crypt(buf,salt));

                            train_t  cryptpasswd_train;
                            cryptpasswd_train.length = sizeof(cryptpasswd);
                            strcpy(cryptpasswd_train.buff,cryptpasswd);
                            salt_train._cmdType = 7;

                            int send_reg_salt =send(sockfd,&salt_train,sizeof(salt_train),MSG_WAITALL);
                            int send_reg_cryptpasswd = send(sockfd,&cryptpasswd_train,sizeof(cryptpasswd_train),MSG_WAITALL);
                        }
                    }
                    //  ································ 用户登录功能 ···································· //
                    else if(flag == 2){

                    }
                }
                else{   //      车厢内容为命令返回值时
                    write(STDOUT_FILENO,rec_train.buff,rec_train.length);
                }
                //  读数据
                if(rec_train.length == 0){
                    printf("对方断开连接 \n");
                    goto end;
                }
                //  write(STDOUT_FILENO,buf,sizeof(buf));
                //write(STDOUT_FILENO,buf,sizeof(buf));
            }
            }
        }

end:
        close(sockfd);

        return 0;
    }

